﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System;
using System.Threading.Tasks;

public class TCPSocket : ClientSocket
{
    [Tooltip("连接上的EndPoint")]
    public string ipEndPoint;

    public override bool connected => m_Socket != null && m_Socket.Connected;
    
    public bool autoReconnect = true;
    
    private List<TCPPacket> m_TCPPackets = new List<TCPPacket>();

    void Awake()
    {
        m_Buffer = new byte[TCPPacket.maxSendTotalOneTime];
    }
    
    protected override void ConnectSocket(string address, int port)
    {
        autoReconnect = true;
        ConnectSocketTCP(address, port);
    }

    private int maxTryConnectCount = 10;
    
    void ConnectSocketTCP(string address, int port)
    {
        try
        {
            /*if (CheckNetworkVersion6(address))
            {
                GameDebug.LogShowy ( "[ socket network ]------ipv6------" );
                m_Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); 
            }
            else
            {
                GameDebug.LogShowy ( "[ socket network ]------ipv4------" );
                m_Socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            }*/
            /*IPv6SupportMidleware.getIPType(address, port.ToString(), out string newServerIp, out newAddressFamily);
            if (!string.IsNullOrEmpty(newServerIp))
            {
                address = newServerIp;
            }*/

            TryParseHost(address, out IPAddress ipAddress);
            AddressFamily newAddressFamily = ipAddress.AddressFamily;

            var remoteEndPoint = new IPEndPoint(ipAddress, port);

            GameDebug.LogShowy("TCP Socket AddressFamily :" + newAddressFamily.ToString() + "    Server host:" + address + $"  ipAddress:{ipAddress}");
            
            m_Socket = new Socket(newAddressFamily,SocketType.Stream, ProtocolType.Tcp);
            
            SocketAsyncEventArgs socketEventArgs = new SocketAsyncEventArgs();
            socketEventArgs.RemoteEndPoint =remoteEndPoint;
            socketEventArgs.Completed += OnConnectCompleted;
            bool willRaiseEvent = m_Socket.ConnectAsync(socketEventArgs);
            if (!willRaiseEvent)
            {
                GameDebug.LogError("连接失败");
                socketEventArgs.Completed -= OnConnectCompleted;
                socketEventArgs.Dispose();
                
                OnConnectComplete(m_Socket);
            }
        }
        catch (Exception e)
        {
            GameDebug.LogError(e);
            
            OnConnectComplete(null);
        }
        
    }
    
    /// <summary>
    /// 连接完成
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="socketEventArgs"></param>
    void OnConnectCompleted(object sender, SocketAsyncEventArgs socketEventArgs)
    {
        GameDebug.Log($"连接完成  state:{state} {socketEventArgs.ConnectSocket}  {socketEventArgs.ConnectSocket?.Connected}");

        OnConnectComplete(socketEventArgs.ConnectSocket);

        ipEndPoint = socketEventArgs.ConnectSocket.RemoteEndPoint?.ToString();
    }
    
    private int m_ConnectTryCount = 0;
    protected void OnConnectComplete(Socket connectSocket)
    {
        GameDebug.Log($"连接完成  state:{m_State} {connectSocket}  {connectSocket?.Connected}");

        m_ConnectTryCount++;
        
        m_Socket = connectSocket;
        
        bool reConnect = m_State == State.TryReconnect;
        
        if (m_Socket != null && m_Socket.Connected)
        {
            m_State = State.Established;

            GameDebug.Log("开始接收");
            
            BeginReceive();

            m_ConnectTryCount = 0;
        }
        else
        {
            GameDebug.Log("连接失败");
            if (autoReconnect)
            {
                if (reConnect)
                {
                    //断线重连
                    Disconnect();
                }
                else
                {
                    if (m_ConnectTryCount > maxTryConnectCount)
                    {
                        Disconnect();
                        autoReconnect = false;
                    }
                    else if (this)
                    {
                        GameDebug.Log("初次未连接成功，再次连接");
                        BeginConnect(m_RemoteIPAddress, m_RemotePort);
                    }
                }
            } 
            else
            {
                this.socketProcess?.OnDisconnect();
            }

            return;
        }
        
        if (reConnect)
        {
            GameDebug.Log("ClientSocket OnReConnect");
            UnityThread.PostAction(() =>
            {
                socketProcess?.OnReConnect();
            });
        }
        else
        {
            GameDebug.Log($"ClientSocket OnConnect  :{socketProcess != null} socketProcess:{socketProcess != null}");
            if(socketProcess != null)
            { 
                UnityThread.PostAction(() =>
                {
                    socketProcess.OnConnect();
                });
            }
        }
    }


    private const bool receiveAsync = true;//是否异步接收
    private Thread syncReceiveThread;

    protected override void _BeginReceive()
    {
        if (!connected)
        {
            GameDebug.LogError("TCP _BeginReceive not connected");
            return;
        }
        
        //System.Array.Clear(m_Buffer,0,m_Buffer.Length);
        if (receiveAsync)
        {
            IAsyncResult result =  m_Socket.BeginReceive(m_Buffer, 0, m_Buffer.Length, SocketFlags.None, OnReceiveData, null);
        }
        else
        {
            syncReceiveThread = new Thread(ReceiveSync);
            syncReceiveThread.Start();
        }        
    }

    void ReceiveSync()
    {
        while (m_Socket != null && m_Socket.Connected)
        {
            //GameDebug.LogError("开始接收");

            try
            {
                int receiveCount = m_Socket.Receive(m_Buffer);
                if (receiveCount == 0) // 连接已关闭
                {
                    GameDebug.Log("收到关闭信号");
                    this.Disconnect();
                    break;
                }
                else
                {
                    GameDebug.Log($"TCP 收到数据长度：{receiveCount}   :\n");
                    ReceivedData(receiveCount);
                }
            }
            catch (Exception e)
            {
                GameDebug.LogError(e.Message+"\n"+e.StackTrace);
            }
        }
    }
    /// <summary>
    /// 接收到数据
    /// </summary>
    /// <param name="result"></param>
    void OnReceiveData(IAsyncResult result)
    {
        if (m_Socket == null)
        {
            GameDebug.LogError("m_Socket == null");
            return;
        }
        try
        {
            int receiveCount = m_Socket.EndReceive(result);
            if (receiveCount == 0) // 连接已关闭
            {
                GameDebug.Log("收到关闭信号");
                this.Disconnect();
            }
            else
            {
                //GameDebug.Log($"TCP 收到数据长度：{receiveCount}   :\n");
                ReceivedData(receiveCount);
                
                BeginReceive();
            }
        }
        catch (Exception ex)
        {
            GameDebug.LogError("接收异常: " + ex);

            this.Disconnect();
            return;
        }
    }

    /*async Task*/void ReceivedData(int receiveCount)
    {
        if (receiveCount == 1 && m_Buffer[0] == ServerPingByte)
        {
            //GameDebug.Log($"Server Ping");
            this._SendData(new byte[] {ServerPingByte}, null);
            return;
        }

        
        byte[] buff = m_Buffer;
        byte[] newData = new byte[receiveCount];
        Array.Copy(buff, newData, receiveCount);
 #if UNITY_EDITOR
        //GameDebug.Log($"TCP 收到数据：{BitConverter.ToString(newData)}   :\n");
#endif

        m_OnReceiveData?.Invoke(newData, m_Socket.RemoteEndPoint);

        //);
        /*await Task.Run(() =>
        {
            //GameDebug.LogError($"TCP 处理数据开始\n");
            //GameDebug.LogError($"TCP 处理数据结束\n");
        });*/
    }
    
    private float m_LastReceiveDataTime;
    /*
    protected override void OnReceiveData(byte[] newData, EndPoint endPoint)
    {     
        int id = BitConverter.ToInt32(newData,0);

        TCPPacket packet = m_TCPPackets.Find(e => e.id == id);
        if (packet == null)
        {
            TCPPacket newPacket = new TCPPacket(id);
            
            newPacket.OnReceivePacket(newData);
            
            if (newPacket.receiveEnd)
            {
                GameDebug.LogError($"新包处理数据：{id}");
                OnProcessData(newPacket.data);
            }
            else
            {
                GameDebug.LogError($"新包加入：{id}");

                m_TCPPackets.Add(newPacket);
            }
        }
        else
        {
            packet.OnReceivePacket(newData);
                
            if (packet.receiveEnd)
            {
                OnProcessData(packet.data);
#if DEBUG
                GameDebug.LogError($"接收包完成");
#endif
                m_TCPPackets.Remove(packet);
            }
        }
    }
    */
    

    const int maxLengthSendATime = TCPPacket.maxSendContentLengthOneTime;
    
    private int m_Seq;
    protected int GetCurSeqId()
    {
        return System.Threading.Interlocked.Increment(ref m_Seq);
    }

    /*
    protected override void SendData(byte[] packet, object messageToken)
    {
        int packetId = GetCurSeqId();

        byte[] bytes = new byte[packet.Length];
        System.Array.Copy(packet, bytes, packet.Length);

        Queue<byte[]> m_SendQueue = new Queue<byte[]>();

        int index = 0;
        while (bytes.Length > maxLengthSendATime)
        {
            byte[] block = new byte[maxLengthSendATime];
            Array.Copy(bytes, block, maxLengthSendATime);

            m_SendQueue.Enqueue(block);

            index += maxLengthSendATime;

            bytes = new byte[bytes.Length - maxLengthSendATime];
            Array.Copy(packet, index, bytes, 0, bytes.Length);
        }

        m_SendQueue.Enqueue(bytes);

        int sendIndex = 0;
        while (m_SendQueue.Count > 0)
        {
            byte[] sendData = m_SendQueue.Dequeue();

            byte[] headBytes = BitConverter.GetBytes(packetId);
            byte[] indexBytes = BitConverter.GetBytes(sendIndex);

            string tail = "----"; 
            byte[] tailBytes = System.Text.Encoding.UTF8.GetBytes(tail);

            int dataLength = sendData.Length;
            int headCount = headBytes.Length;
            int indexCount = indexBytes.Length;
            int tailCount = tailBytes.Length;

            if (m_SendQueue.Count == 0)
            {
                tail = UDPPacket.Tail;
                tailBytes = System.Text.Encoding.UTF8.GetBytes(tail);
                //GameDebug.Log($"发送尾包");
            }

            byte[] data = new byte[headCount + dataLength + indexCount + tailCount];

            System.Array.Copy(headBytes, 0, data, 0, headCount);
            System.Array.Copy(indexBytes, 0, data, headCount, indexCount);
            System.Array.Copy(sendData, 0, data, headCount + indexCount, dataLength);
            if (tailCount > 0)
                System.Array.Copy(tailBytes, 0, data, headCount + indexCount + dataLength, tailCount);
/*#if DEBUG
            GameDebug.Log(
                $"发送 sendIndex:{sendIndex}  packetHeadCount:{headCount}   dataLength:{data.Length}  tailCount：{tailCount}  endPoint:{m_RemoteEndPoint}  ");
#endif#1#
            this._SendData(data,messageToken);

            Thread.Sleep(10);

            sendIndex++;
        }
    }
    */

    public override void _SendData(byte[] data, object messageToken)
    {
        if (!this.connected)
        {
            GameDebug.LogError("TCPSocket _SendData m_Socket not Connected");
            return;
        }
        //GameDebug.Log($"send data :{BitConverter.ToString(data)}");
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();
        e.Completed += OnSendAsyncCompleted;
        e.UserToken = messageToken;
        e.SetBuffer(data, 0, data.Length);

        bool ioPending = m_Socket.SendAsync(e);
        if (!ioPending)
        {
            OnSendAsyncCompleted(this, e);
        }
    }

    /// <summary>
    /// 发送结束的回调（若服务器立刻返回数据，可能会在收到数据之后才进入此方法）
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnSendAsyncCompleted(object sender, System.Net.Sockets.SocketAsyncEventArgs e)
    {
        //GameDebug.Log("OnSendAsyncCompleted");

        //if (this.m_SocketProcess != null)
        //{
        //    this.m_SocketProcess.OnMessageSend(e.UserToken, e.SocketError);
        //}
    }
    
      
    float m_LastTryConnectTime;

    /// <summary>
    /// 检查连接状态
    /// </summary>
    protected void CheckConnection()
    {
        if (state == State.Closed) 
        {
            if(autoReconnect)
            {
                if (Time.realtimeSinceStartup - m_LastTryConnectTime > 3)
                {
                    m_LastTryConnectTime = Time.realtimeSinceStartup;

                    GameDebug.Log("断开 尝试重连");
                    
                    ReConnect();
                }
            }
        }
        else if(state != State.Connecting && state != State.TryReconnect)
        {
            if (!connected)
            {
                this.Disconnect();
            }
        }
    }
    
    /// <summary>
    /// 重连
    /// </summary>
    protected virtual void ReConnect()
    {
        GameDebug.Log("尝试 ReConnect");
        
        m_State = State.TryReconnect;
        
        //CloseClient();
        this.ConnectSocket(m_RemoteIPAddress, m_RemotePort);
    }
  

    private void OnApplicationPause(bool pause)
    {
/*        if (!pause)
        {
#if !UNITY_EDITOR
            GameDebug.Log("Focus Relogin");
            ReConnect();
#endif
        }*/
    }

    protected override void OnDestroy()
    {
        base.OnDestroy();
        
        syncReceiveThread?.Abort();
    }

    protected override void Update()
    {
        base.Update();

        CheckConnection();
    }
}

